use actix_web::{web, HttpRequest, HttpResponse, Result};
use uuid::Uuid;
use serde::{Deserialize, Serialize};
use validator::Validate;

use crate::{
    database::Database,
    error::{AppError, AppResult},
    middleware::auth::{get_user_claims, Claims},
    models::user::*,
    services::UserService,
    utils::validation::validate_request,
};

#[derive(Deserialize, Validate)]
pub struct ListUsersQuery {
    #[validate(range(min = 1, max = 100, message = "Page must be between 1 and 100"))]
    pub page: Option<u64>,
    
    #[validate(range(min = 1, max = 100, message = "Per page must be between 1 and 100"))]
    pub per_page: Option<u64>,
}

#[derive(Serialize)]
pub struct ListUsersResponse {
    pub users: Vec<UserResponse>,
    pub pagination: PaginationInfo,
}

#[derive(Serialize)]
pub struct PaginationInfo {
    pub current_page: u64,
    pub per_page: u64,
    pub total_items: u64,
    pub total_pages: u64,
}

pub async fn get_user(
    path: web::Path<Uuid>,
    req: HttpRequest,
    db: web::Data<Database>,
) -> Result<HttpResponse> {
    let user_id = path.into_inner();
    let claims = get_user_claims(&req)
        .ok_or_else(|| AppError::unauthorized("Invalid token"))?;
    
    // Users can only view their own profile unless they're admin
    if claims.sub != user_id && claims.role != "admin" {
        return Err(AppError::forbidden("Access denied").into());
    }
    
    let user_service = UserService::new((**db).clone());
    
    match user_service.find_by_id(user_id).await {
        Ok(Some(user)) => Ok(HttpResponse::Ok().json(UserResponse::from(user))),
        Ok(None) => Err(AppError::not_found("User not found").into()),
        Err(e) => Err(e.into()),
    }
}

pub async fn update_user(
    path: web::Path<Uuid>,
    req_body: web::Json<UpdateUserRequest>,
    req: HttpRequest,
    db: web::Data<Database>,
) -> Result<HttpResponse> {
    let user_id = path.into_inner();
    let claims = get_user_claims(&req)
        .ok_or_else(|| AppError::unauthorized("Invalid token"))?;
    
    // Users can only update their own profile unless they're admin
    if claims.sub != user_id && claims.role != "admin" {
        return Err(AppError::forbidden("Access denied").into());
    }
    
    // Validate request
    validate_request(&req_body.0)?;
    
    let user_service = UserService::new((**db).clone());
    
    match user_service.update_user(user_id, req_body.into_inner()).await {
        Ok(user) => Ok(HttpResponse::Ok().json(UserResponse::from(user))),
        Err(e) => Err(e.into()),
    }
}

pub async fn delete_user(
    path: web::Path<Uuid>,
    req: HttpRequest,
    db: web::Data<Database>,
) -> Result<HttpResponse> {
    let user_id = path.into_inner();
    let claims = get_user_claims(&req)
        .ok_or_else(|| AppError::unauthorized("Invalid token"))?;
    
    // Only admins can delete users, and they can't delete themselves
    if claims.role != "admin" {
        return Err(AppError::forbidden("Only admins can delete users").into());
    }
    
    if claims.sub == user_id {
        return Err(AppError::bad_request("Cannot delete your own account").into());
    }
    
    let user_service = UserService::new((**db).clone());
    
    match user_service.delete_user(user_id).await {
        Ok(_) => Ok(HttpResponse::NoContent().finish()),
        Err(e) => Err(e.into()),
    }
}

pub async fn list_users(
    query: web::Query<ListUsersQuery>,
    req: HttpRequest,
    db: web::Data<Database>,
) -> Result<HttpResponse> {
    let claims = get_user_claims(&req)
        .ok_or_else(|| AppError::unauthorized("Invalid token"))?;
    
    // Only admins can list all users
    if claims.role != "admin" {
        return Err(AppError::forbidden("Only admins can list users").into());
    }
    
    // Validate query parameters
    validate_request(&query.0)?;
    
    let page = query.page.unwrap_or(1);
    let per_page = query.per_page.unwrap_or(10);
    
    let user_service = UserService::new((**db).clone());
    
    match user_service.list_users(page, per_page).await {
        Ok((users, total)) => {
            let total_pages = (total + per_page - 1) / per_page; // Ceiling division
            
            let response = ListUsersResponse {
                users: users.into_iter().map(UserResponse::from).collect(),
                pagination: PaginationInfo {
                    current_page: page,
                    per_page,
                    total_items: total,
                    total_pages,
                },
            };
            
            Ok(HttpResponse::Ok().json(response))
        }
        Err(e) => Err(e.into()),
    }
}

pub async fn activate_user(
    path: web::Path<Uuid>,
    req: HttpRequest,
    db: web::Data<Database>,
) -> Result<HttpResponse> {
    let user_id = path.into_inner();
    let claims = get_user_claims(&req)
        .ok_or_else(|| AppError::unauthorized("Invalid token"))?;
    
    // Only admins can activate/deactivate users
    if claims.role != "admin" {
        return Err(AppError::forbidden("Only admins can activate users").into());
    }
    
    let user_service = UserService::new((**db).clone());
    
    match user_service.activate_user(user_id).await {
        Ok(user) => Ok(HttpResponse::Ok().json(UserResponse::from(user))),
        Err(e) => Err(e.into()),
    }
}

pub async fn deactivate_user(
    path: web::Path<Uuid>,
    req: HttpRequest,
    db: web::Data<Database>,
) -> Result<HttpResponse> {
    let user_id = path.into_inner();
    let claims = get_user_claims(&req)
        .ok_or_else(|| AppError::unauthorized("Invalid token"))?;
    
    // Only admins can activate/deactivate users
    if claims.role != "admin" {
        return Err(AppError::forbidden("Only admins can deactivate users").into());
    }
    
    // Admins can't deactivate themselves
    if claims.sub == user_id {
        return Err(AppError::bad_request("Cannot deactivate your own account").into());
    }
    
    let user_service = UserService::new((**db).clone());
    
    match user_service.deactivate_user(user_id).await {
        Ok(user) => Ok(HttpResponse::Ok().json(UserResponse::from(user))),
        Err(e) => Err(e.into()),
    }
}

pub async fn get_user_profile(
    req: HttpRequest,
    db: web::Data<Database>,
) -> Result<HttpResponse> {
    let claims = get_user_claims(&req)
        .ok_or_else(|| AppError::unauthorized("Invalid token"))?;
    
    let user_service = UserService::new((**db).clone());
    
    match user_service.find_by_id(claims.sub).await {
        Ok(Some(user)) => Ok(HttpResponse::Ok().json(UserResponse::from(user))),
        Ok(None) => Err(AppError::not_found("User not found").into()),
        Err(e) => Err(e.into()),
    }
}

pub async fn update_user_profile(
    req_body: web::Json<UpdateUserRequest>,
    req: HttpRequest,
    db: web::Data<Database>,
) -> Result<HttpResponse> {
    let claims = get_user_claims(&req)
        .ok_or_else(|| AppError::unauthorized("Invalid token"))?;
    
    // Validate request
    validate_request(&req_body.0)?;
    
    let user_service = UserService::new((**db).clone());
    
    match user_service.update_user(claims.sub, req_body.into_inner()).await {
        Ok(user) => Ok(HttpResponse::Ok().json(UserResponse::from(user))),
        Err(e) => Err(e.into()),
    }
}

pub fn configure(cfg: &mut web::ServiceConfig) {
    cfg.service(
        web::scope("/users")
            .route("", web::get().to(list_users))
            .route("/profile", web::get().to(get_user_profile))
            .route("/profile", web::put().to(update_user_profile))
            .route("/{id}", web::get().to(get_user))
            .route("/{id}", web::put().to(update_user))
            .route("/{id}", web::delete().to(delete_user))
            .route("/{id}/activate", web::post().to(activate_user))
            .route("/{id}/deactivate", web::post().to(deactivate_user))
    );
}

#[cfg(test)]
mod tests {
    use super::*;
    use actix_web::{test, App};
    use crate::config::AppConfig;

    #[actix_web::test]
    async fn test_list_users_query_validation() {
        let config = AppConfig::default();
        let app = test::init_service(
            App::new()
                .app_data(web::Data::new(config))
                .configure(configure)
        ).await;

        // Test with invalid page parameter
        let req = test::TestRequest::get()
            .uri("/users?page=0") // Invalid: page must be >= 1
            .to_request();
            
        let resp = test::call_service(&app, req).await;
        // This would fail due to missing authentication, but validation would also fail
        assert!(!resp.status().is_success());
    }
}